/*
SimpleFS

This file will contain function and object prototypes along with any
constants
*/

#ifndef SIMPLEFS_H
#define SIMPEFS_H

#include <ctime>
#include <cstdlib>
#include <stdio.h>  //What are these C libraries being used for??
#include <stdlib.h>
#include <iostream>
// #include <fstream>
#include <string>
#include <set>
#include <stack>
#include <deque>
#include <map>
#include <sstream>
#include "our_fstream.h"
#include <windows.h>  // for certain windows system calls?

using namespace std;

#define WINDOWS_DEV true    // later we can make this false

// afr: "Allen", "Frank", "Robertson"
#define FS_NAME "560_filesystem_afr.dat" 
#define FS_SIZE 1024 * 1024 * 100     // # of total bytes (adjust as needed during development)
#define DS_PTRS_SIZE 8	  // space taken by the ptrs at beginning of fs for disk data structures (DS)
#define	FILE_SEG_INFO_SIZE 8 // space taken for info about each file segment (data block)
#define BLOCK_NEXT_PTR_POS 4 // offset location of next data block for a file (rel to beg of the block)
#define BLOCK_PREV_PTR_POS 0 // offset location of prev data block for a file (rel to beg of the block)

#define FREE_HEAD_PTR_POSITION 4 // Offset where the free-head pointer resides (# bytes from beg of fs)
#define ROOT_INODE_PTR_POSITION 0  // Offset of the 4 bytes that hold offset of the root inode
// need constant sized names so Inodes are const size (so we know how many bytes to read from disk)
#define MAX_FILENAME_SZ 64
#define BLOCK_SZ 128   // can change this later (keeping small for easier debugging)
#define test_mkfs true   // easy way to turn on/off test print statments, etc (can have 1 for each func)
#define test_fs_link false
#define test_update_all_links false
// read/write permissions
#define FS_O_RDONLY 0
#define FS_O_WRONLY 1
#define FS_O_RDWR 2
//////////////////////////
// Main GLOBALS //////////

// fs: "file system". I/O object used by all main cmds
static our_fstream fs;   // open for duration of a shell's exec for good perf & ease of programming

//cwd: "Current Working Directory"
//a stack is used to keep up with where we've been in the fs
static stack<unsigned int> cwd;
static deque<string> cwd_name;

static unsigned int free_head;    // offset "points" to first node of free-list
static bool free_list_empty;    // gets set to true when the last free-list node gets taken

// early version for testing 
class Inode {

	public:
		char name[MAX_FILENAME_SZ];
		bool dir;    // indicates whether or not its a directory
		unsigned int size;
		time_t modified;
		unsigned int children; //for directories
		unsigned int data; //for files
		unsigned int eof; //last byte of data on disk for files
		unsigned int parent; //parent directory
		unsigned int next; //next sibling in current directory
		unsigned int prev; // previous sibling
		unsigned int location; //location of the inode on disk  //makes things a little easier
		unsigned int eof_block_pos; //position of the last block in the file, indexed from the beginning 
								//of the last block
		int link_count; // 1 means only 1 inode points to the data. 2 means 2 inodes point to same data...
		               // If link_count reaches 0, "unlink" has been called on all files, so delete data too
		unsigned int link_next;  // for linked list of linked files
		unsigned int link_prev;
		
		// num data blocks (for files. 0 for dirs?)
		int num_blocks;  // Easier to include this field and increment it, than to rely on using math
		                 //  and remember to account for all the bookkeeping bytes used by various things
};


// struct for users who call fs_stat(). stat() will fill in the struct that they pass in
typedef struct {
	char name[MAX_FILENAME_SZ];
	bool is_dir;    // indicates whether or not its a directory
	unsigned int size;  // # bytes. For dirs, its the # bytes of all its decendents?
	unsigned int num_blocks;  // for files only
	time_t modified;
	int link_count;

} fileStat;


//Open file table entry
struct Oft_entry
{

	Inode * inode; //inode associated with the file
	int flag; //read/write permissions
	//I can't think of any reason we would need this
	//unsigned int f_offset; //offset within the file
	unsigned int d_offset; //offset within the virtual "harddisk"
	unsigned int block_pos; //offset of the d_offset within the current block

};

//Open file table
static map<int, Oft_entry> oft;
// end globals //////
/////////////////////

/* ////////////////////////////
	 ////	CARVE stuff ///////////
		Created Date:  2/14/11
		Author:      Jason Frank

		Purpose: Basically mimics what malloc() and free() do for the memory, but do it on disk.
		         Unlike malloc, this does not do buffering.  Note that this file includes a coalesce
						 function that can be called anytime free() is called.
*/

// Free-list node struct
typedef struct flist  {
	unsigned int size;   
	unsigned int flink;  // "points" to next node (# bytes from beg of file)
	unsigned int blink;  // to previous node

}  FList_node;
// "Public" functions
unsigned int carve( unsigned int size );
void my_free(  unsigned int user_offset ); 
// "Private" functions
void read_flist_nd( unsigned int offset, FList_node* f, string calling_func_name);
void write_flist_nd(unsigned int offset, FList_node* f, string calling_func_name);
void delete_node( FList_node* f ); // remove it from the list
void add_node( unsigned int new_node_offset);
void print_FList();
int carve_test_main();

static set <unsigned int> test_offsets;  // for testing purposes 

// void* free_list_begin();
//void* free_list_next(void* node);
//void  coalesce_free_list();

// carve globals (from mymalloc.c)
#define BOOKKEEP 8        
#define FNODE_SIZE 12

// END CARVE stuff /////////////
////////////////////////////////

////////////////////////////////
//// HELPER functions //////////

int init_return_user();
int mkfs_handle_errors_keep_file_open(string calling_func_name);
int verify_fs_open(string calling_func_name);
// return the string of path minus the last part. PROB WON'T WORK IF PATH==ROOT
string get_parent_path(char* path);  
// return the filename part of a pathname
string get_filename_from_path(const char* path);

/*
This function searches the cwd for the inode of a file or directory
and returns the offset of its position in the "virtual harddrive".
*/
Inode * get_inode(const char * name);
/*
  Return the offset of the inode of the dir or file specified (or 0 if not found).
  "Pathname" can be either relative or absolute.
*/
unsigned int get_offset_from_path(const char * path); // return the offset of the dir or file specified 
// helper for get_offset_from_path()
Inode * get_inode_from_dirOffset(unsigned int dir_offset, const char * filename);
/*
  Updates all relavant file metadata (size, num_blocks, eof, eof_block_pos, modified, 
	link_count, etc) of all files that are linked to the "source_inode" file.  It uses the 
	metadata of "source_inode" as the source of the metadata (sets all other linked files
	metadata to the same as it).
*/
void update_all_links(Inode* source_inode);

// Helper for update_all_links().  Only writes to memory (caller must still save to disk)
void update_one_link(Inode* source_inode, Inode* dest_inode);  // source & destination inodes
/*
  Inserts "new_inode" into the linked list of linked files linked to "old_inode". 
	The "old_inode" must already have its link_prev and link_next fields set. 
	The func uses the link_next and link_prev fields of "old_inode" to navigate the list.
*/
void insert_link_inode(Inode* old_inode, Inode* new_inode);
/*
  Makes a deep copy of source_inode into dest_inode (copies all fields). 
	Requires both	inodes to have already been allocated.  Used in update_all_links(), etc.
*/
void deep_copy_inode(Inode* source_inode, Inode* dest_inode);
/*
 For our own testing. Useful when you want stat info about a file not in cwd (and you don't have
  path info), and you already have ptr to the inode.
*/
void stat_inode(Inode* inode);
/*
  For our testing. Prints the directory structure from the given path in a tree format,
	and prints stat-like info about files (but not dirs).
*/
void tree_stat(const char* path);
void tree_stat_recur(Inode* parent_inode, string tabstr);
/*
   Helper for tree_stat.  Used for formatting.
*/
void print_spaces(int num_spaces);
/*
  Traverse through all links connected to the given inode and print their names
*/
void print_linked_names(Inode* inode);

//// END HELPER Functions /////
///////////////////////////////

///////////////////////////////
// Main project functions /////

/* FS_MKFS
Creates a file system or formats an existing one.
*/
int fs_mkfs();

/* FS_OPEN
This function opens an existing file or creates a new
one, assigns a file descriptor to it, and returns that 
descriptor.  Upon failure, -1 is returned.

If a file is opened with read-only permissions, the r/w pointer is
positioned at the beginning of the file.

If a file is opened with read/write or write-only permissions the 
r/w pointer is positioned at the end of the file.
*/
int fs_open(const char * filename, int flag);

/* FS_CLOSE
This function closes an open file by freeing up the file
descriptor assigned to it.  Upon success, it returns 0.
Upon failure, it returns -1.

This function also has the task of deleting an open file
which has zero links to its data

i.e. An open file cannot be deleted while it is open, but
when it is closed, it must then be deleted.
*/
int fs_close(int fd);

/* FS_READ
Reads a certain number of bytes, given a file descriptor, and prints them to the console.
On success, returns the number of bytes read.
On failure, returns -1.
*/
int fs_read (int fd, char * buf, int count);

/* FS_CAT
Prints the contents of a file.
On success, returns 0.
On failure, returns -1.
*/
int fs_cat(const char * filename);

/* FS_WRITE
Writes bytes to a file, given a file descriptor.
On success, returns the number of bytes written.
On failure, returns -1.
*/
int fs_write (int fd, const char * buf, int count);

/* FS_SEEK
This function repositions the r/w pointer associated with a file descriptor.
If the user attempts to read past the eof, the pointer is positioned at the eof.
On success, the position in the file of the r/w pointer is returned.
On failure, returns -1;
*/
int fs_seek (int fd, int offset);

/* FS_MKDIR
This function creates an empty directy inside the cwd.
If there is already a file or directory by that name,
it will fail.
On success, returns 0.
On failure, returns -1.
*/
int fs_mkdir (const char * dirname);

/* FS_RMDIR
Removes a directory the current working directory.
On success, returns 0.
On failure, returns -1.
*/
int fs_rmdir (const char * dirname);

/* FS_CHDIR
This function changes the current working directory.
On success, returns 0.
On failure, returns -1.
*/
int fs_chdir (const char * dirname);

/* FS_LINK

*/
int fs_link (const char * oldpath, const char * newpath);

/* FS_UNLINK

*/
int fs_unlink (const char * filename);

/* FS_COPY
This function takes to arguments, the path of the 
source file and the path of the destination.
The data of the source is then copied to the newly
created destination.
The source must exist, and the destination must not.
The two paths may both be relative or absolute.
On success, returns 0.
On failure, returns-1.
*/
int fs_copy(const char * src, const char * dest);

/* FS_STAT

*/
int fs_stat (const char * filename, fileStat *buf);

/* FS_IMPORT
This function imports a file from the user's physical hard drive into the filesystem
*/
int fs_import(const char * src, const char * dest);

/* FS_EXPORT
This function exports a file from the filesystem onto the user's physical harddrive
*/
int fs_export(const char * src, const char * dest);


/* NON-RECURSIVE LS
This function facilitates the "ls" command (not recursive-- current dir only)
*/
int print_current_directory();

/* TREE
This function facilitates the "tree" command. This func takes no args to match Dr. Cao's,
specificiations.  The real work gets done by print_dir_recur() below.
*/
void print_directory();
/* RECURSIV LS (USED BY TREE)
This function implements the real meat of the "tree" command (recursive)
*/
void print_dir_recur(Inode * parent_inode, string tabstr);



// End Project Functions /////
//////////////////////////////


/* PRINT WORKING DIRECTORY
Prints out the full path of the current working
directory.
*/
void pwd(ostream & out);

/* CREATE FILE (HELPER & TESTING FN.)
Creates an empty file of a user-specified size.
The data is allocated in blocks of size, BLOCK_SZ,
and are linked together as in a doubly linked list.
On success, returns 0.
On failure, returns -1.
*/
unsigned int create_file(const char * file_name, int size);

#endif